/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package runebound.manejadores;

import java.util.logging.Level;
import java.util.logging.Logger;
import runebound.combate.FaseCombate;
import runebound.combate.LineaRegistroString;
import runebound.combate.PlanCombate;
import runebound.combate.RBCalculadoraCombate;
import runebound.combate.RBRegistroInformacion;
import runebound.manejadores.eventos.RBEventoInicioCombate;
import runebound.manejadores.eventos.RBEventoNuevoTurno;
import runebound.manejadores.eventos.combate.RBEventoCombate;
import runebound.manejadores.eventos.combate.RBEventoCombatePlanificado;
import runebound.manejadores.eventos.combate.RBEventoDarRecompensa;
import runebound.manejadores.eventos.combate.RBEventoDefensaExito;
import runebound.manejadores.eventos.combate.RBEventoDefensaFallo;
import runebound.manejadores.eventos.combate.RBEventoFaseCombate;
import runebound.manejadores.eventos.combate.RBEventoFinRondaCombate;
import runebound.manejadores.eventos.combate.RBEventoHeroeMuerto;
import runebound.manejadores.eventos.combate.RBEventoHit;
import runebound.manejadores.eventos.combate.RBEventoHuir;
import runebound.manejadores.eventos.combate.RBEventoMounstroMuerto;
import runebound.manejadores.eventos.combate.RBEventoTestResultSinAccion;
import runebound.manejadores.eventos.combate.RBEventoTestResultado;
import runebound.modelo.heores.Heroe;
import runebound.modelo.monstruos.Monstruo;
import runebound.random.RBAzar;
import runebound.random.Tirada;
import runebound.ui.combate.RBCombateFrame;
import runebound.ui.combate.RBSubirNivelDialog;

/**
 *
 * @author Miguel (alakat@gmail.com)
 */
public class RBControladorCombate {

    private final Object lock = new Object();
    private Heroe heroe;
    private Monstruo monstruo;
    private RBCombateFrame frame;
    private FaseCombate fase;
    private PlanCombate plan;

    private static final Logger LOG = Logger.getLogger(RBControladorCombate.class.getName());

    public RBControladorCombate(Heroe heroe, Monstruo monstruo, RBCombateFrame frame) {
        this.heroe = heroe;
        this.monstruo = monstruo;
        this.frame = frame;
        this.fase = FaseCombate.ESPERA;

    }

    public void recibirEventoCombate( RBEventoCombate evento) {
        if (evento instanceof RBEventoInicioCombate) {
            this.inicioCombate((RBEventoInicioCombate) evento);
        } else if (evento instanceof RBEventoTestResultado) {
            this.antesCombateResuleto((RBEventoTestResultado) evento);
        } else if (evento instanceof RBEventoCombatePlanificado) {
            this.inicioRondaCombate((RBEventoCombatePlanificado) evento);
        } else if (evento instanceof RBEventoFaseCombate) {
            this.procesarFaseCombate((RBEventoFaseCombate) evento);
        } else if (evento instanceof RBEventoHit) {
            this.procesarHit((RBEventoHit) evento);
        } else if (evento instanceof RBEventoDefensaExito) {
            this.procesarDefensaExito((RBEventoDefensaExito) evento);
        } else if (evento instanceof RBEventoDefensaFallo) {
            this.procesarDefensaFallo((RBEventoDefensaFallo) evento);
        } else if (evento instanceof RBEventoFinRondaCombate) {
            this.procesarFinRondaCombate((RBEventoFinRondaCombate) evento);
        } else if (evento instanceof RBEventoMounstroMuerto){
            this.procesarMonstruoMuerto((RBEventoMounstroMuerto)evento);
        } else if( evento instanceof RBEventoHeroeMuerto){
            this.procesarHeroeMuerto((RBEventoHeroeMuerto)evento);
        } else if(evento instanceof RBEventoDarRecompensa){
            this.procesarRecompensa((RBEventoDarRecompensa)evento);
        } else if(evento instanceof RBEventoHuir){
            this.procesarHuida((RBEventoHuir)evento);
        }
    }

    /**
     * Realizamos la configuración pertinente para el arranque del combate
     *
     * @param rbEventoInicioCombate
     */
    private void inicioCombate(RBEventoInicioCombate evento) {
        
        this.frame.setCombate(monstruo, heroe);
        this.frame.setVisible(true);
    }

    private void antesCombateResuleto(RBEventoTestResultado evento) {
        if(evento instanceof RBEventoTestResultSinAccion){
            //Tada que aplicar
        }else{
            frame.updateDatosCombate();
            synchronized(this.lock){
            try {
                this.lock.wait(2000);
            } catch (InterruptedException ex) {
                Logger.getLogger(RBControladorCombate.class.getName()).log(Level.SEVERE, null, ex);
            }
            }
        }

        frame.iniciarCombate();

    }

    /**
     * Asigna el plan e inicia el combate
     *
     * @param rbEventoCombatePlanificado
     */
    private void inicioRondaCombate(RBEventoCombatePlanificado rbEventoCombatePlanificado) {
        this.plan = rbEventoCombatePlanificado.getPlanCombate();
        if (plan.isHuir()) {
            RBControladorJuego.getInstance().recibirEvento(new RBEventoFaseCombate(FaseCombate.HUIR));
        } else {
            RBControladorJuego.getInstance().recibirEvento(new RBEventoFaseCombate(FaseCombate.DISPARO));
        }
    }

    private void procesarFaseCombate(RBEventoFaseCombate eventoCombate) {
        RBEventoCombate nuevoEvento = null;
        this.frame.getrBCombatePanel().getrBDuranteCombatePanel().setFaseCombate(eventoCombate.getFase());
        boolean exitoHuida=false;
        switch (eventoCombate.getFase()) {
            case HUIR:
                this.registrar("SubFase HUIR");
                exitoHuida=this.subFaseHuir(this.heroe.getValorCombateDistancia(),
                        this.heroe.getFuerzaMente(),
                        this.monstruo.getValorMente(),
                        this.monstruo.getFuerzaMente());
                break;
            case DISPARO:
                this.registrar("SubFase Disparo");
                this.subFaseCombate(this.heroe.getValorCombateDistancia(),
                        this.heroe.getFuerzaMente(),
                        this.monstruo.getValorMente(),
                        this.monstruo.getFuerzaMente(),
                        this.plan.isAtacarDistancia());
                nuevoEvento = new RBEventoFaseCombate(FaseCombate.FISICO);
                break;
            case FISICO:
                this.registrar("SubFase Físico");
                this.subFaseCombate(this.heroe.getValorCombateFisico(),
                        this.heroe.getFuerzaFisico(),
                        this.monstruo.getValorFisico(),
                        this.monstruo.getFuerzaFisico(),
                        this.plan.isAtacarMeele());
                nuevoEvento = new RBEventoFaseCombate(FaseCombate.MAGIA);
                break;
            case MAGIA:
                this.registrar("SubFase Magia");
                this.subFaseCombate(this.heroe.getValorCombateMagia(),
                        this.heroe.getFuerzaMagia(),
                        this.monstruo.getValorMagia(),
                        this.monstruo.getFuerzaMagia(),
                        this.plan.isAtacarMagia());
                nuevoEvento = new RBEventoFinRondaCombate();
                break;

        }
        
        try {
            synchronized (this.lock) {
                this.lock.wait(3000);
            }
        } catch (InterruptedException ex) {
            Logger.getLogger(RBControladorCombate.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.frame.updateDatosCombate();
        
        if(!exitoHuida){
            /** O bien por que no ha habido huida, o bien, por que ha fallado **/
            if(this.chequearFinCombate()){
                if(!monstruo.isVivo()){
                    RBControladorJuego.getInstance().recibirEvento(new RBEventoMounstroMuerto());
                }else{
                    RBControladorJuego.getInstance().recibirEvento(new RBEventoHeroeMuerto());
                }
            }else{
                RBControladorJuego.getInstance().recibirEvento(nuevoEvento);
            }
        }else{
            RBControladorJuego.getInstance().recibirEvento(new RBEventoHuir());
        }
    }

    private void subFaseCombate(int valorHeroe, int fuerzaHeroe, int valorMonstruo, int fuerzaMonstruo, boolean atacar) {
        registrar("H[" + valorHeroe + "]vsM[" + valorMonstruo + "]");
        Tirada tirada = RBAzar.getInstance().tiradaCombate();
        registrar(tirada.toString());
        boolean superaTirada = valorMonstruo < tirada.getTotal() + valorHeroe;
        if (atacar) {

            if (superaTirada) {
                registrar("El heroe ATACA y GOLPEA haciendo " + fuerzaHeroe + " heridas");
                frame.getrBCombatePanel().getrBDuranteCombatePanel().exitoTirada(tirada,false);
                frame.hitMonstruo(fuerzaHeroe);
                RBControladorJuego.getInstance().recibirEvento(new RBEventoHit(monstruo, fuerzaHeroe));

            } else {
                registrar("El heroe ATACA y FALLA recibiendo" + fuerzaMonstruo + " heridas");
                frame.getrBCombatePanel().getrBDuranteCombatePanel().falloTirada(tirada,false);
                frame.hitHeroe(fuerzaMonstruo);
                RBControladorJuego.getInstance().recibirEvento(new RBEventoHit(heroe, fuerzaMonstruo));

            }
        } else {
            if (superaTirada) {
                registrar("El heroe DEFIENDE y salva el golpe");
                frame.getrBCombatePanel().getrBDuranteCombatePanel().exitoTirada(tirada,true);
                RBControladorJuego.getInstance().recibirEvento(new RBEventoDefensaExito(tirada));
            } else {
                registrar("El heroe DEFIENDE y FALLA recibiendo" + fuerzaMonstruo + " heridas");
                frame.getrBCombatePanel().getrBDuranteCombatePanel().falloTirada(tirada,true);
                frame.hitHeroe(fuerzaMonstruo);
                RBControladorJuego.getInstance().recibirEvento(new RBEventoDefensaFallo(fuerzaMonstruo, tirada));
            }
        }
    }

    private void registrar(String cadena) {
        RBRegistroInformacion.getInstance().registro(new LineaRegistroString(cadena));
    }

    /**
     *
     * Procesa el impacto durante un ATAQUE. O bien se golpea al monstruo o bien
     * al heroe
     *
     * @param eventoHit
     */
    private void procesarHit(RBEventoHit eventoHit) {
        if (eventoHit.getObjetivo() instanceof Monstruo) {
            registrar("Monstruo recibe " + eventoHit.getHeridas() + " heridas");
            monstruo.hit(eventoHit.getHeridas());
        } else {
            registrar("El heroe recibe" + eventoHit.getHeridas() + " heridas");
            heroe.hit(eventoHit.getHeridas());
        }
    }

    /**
     * Procesa el evento de una defensa con éxito
     *
     * @param evento
     */
    private void procesarDefensaExito(RBEventoDefensaExito evento) {
        ////Ya se ha avisado al panel
        //this.frame.getrBCombatePanel().getrBDuranteCombatePanel().exitoTirada(evento.getTirada(),true);
    }

    /**
     * Porcesa el evento de un fallo en defensa
     *
     * @param evento
     */
    private void procesarDefensaFallo(RBEventoDefensaFallo evento) {
        //Ya se ha avisado al panel
        //this.frame.getrBCombatePanel().getrBDuranteCombatePanel().falloTirada(evento.getTirada(),true);
        this.heroe.hit(evento.getHeridas());
        
    }

    /**
     * Porcesa el final de una ronda de combate para
     *
     * @param rbEventoFinRondaCombate
     */
    private void procesarFinRondaCombate(RBEventoFinRondaCombate rbEventoFinRondaCombate) {
        this.frame.nuevaRondaCombate();
    }

    private boolean chequearFinCombate() {
        
        return !heroe.isVivo()||!this.monstruo.isVivo();
    }

    private void procesarMonstruoMuerto(RBEventoMounstroMuerto rbEventoMounstroMuerto) {
        RBControladorJuego.getInstance().getWindow().getMapaPanel().getTileActivo().aventuraAcabada();
        this.frame.finCombate();
        RBControladorJuego.getInstance().getWindow().repaint();
    }

    private void procesarHeroeMuerto(RBEventoHeroeMuerto rbEventoHeroeMuerto) {
        this.frame.setVisible(false);
        RBControladorJuego.getInstance().muerteHeroe(this.heroe);
        RBControladorJuego.getInstance().recibirEvento(new RBEventoNuevoTurno());
    }

    public RBCombateFrame getFrame() {
        return frame;
    }

    private void procesarRecompensa(RBEventoDarRecompensa evento) {
        evento.getRecompensa().asignarRecompensa(monstruo, heroe);
        this.heroe.ganarExperiencia(monstruo.getTipoAmenaza());
        this.frame.setVisible(false);
        //Chequeamos la subida de nivel
        if(RBControladorJuego.getInstance().isSubirNivel()){
            RBSubirNivelDialog dialogo = new RBSubirNivelDialog(frame, true);
            dialogo.setVisible(true);
        }else{
            RBControladorJuego.getInstance().recibirEvento(new RBEventoNuevoTurno());
        }
    }

    /**
     * Procesa la tirada de huida del "héroe"
     * @param valorHeroe
     * @param fuerzaHeroe
     * @param valorMonstruo
     * @param fuerzaMonstruo
     * @return 
     */
    private boolean subFaseHuir(int valorHeroe, int fuerzaHeroe, int valorMonstruo, int fuerzaMonstruo) {
        registrar("H[" + valorHeroe + "]vsM[" + valorMonstruo + "]");
        Tirada tirada = RBAzar.getInstance().tiradaCombate();
        registrar(tirada.toString());
        boolean superaTirada = valorMonstruo < tirada.getTotal() + valorHeroe;
        if(superaTirada){
            registrar("El heroe Logra huir!");
                frame.getrBCombatePanel().getrBDuranteCombatePanel().exitoHuida(tirada);
                

            } else {
                registrar("El heroe NO logra huir recibiendo" + fuerzaMonstruo + " heridas");
                frame.getrBCombatePanel().getrBDuranteCombatePanel().fallohuida(tirada);
                frame.hitHeroe(fuerzaMonstruo);
                RBControladorJuego.getInstance().recibirEvento(new RBEventoHit(heroe, fuerzaMonstruo));
        }
        return superaTirada;
    }

    /**
     * Procesa un huida a tiempo
     * @param evento 
     */
    private void procesarHuida(RBEventoHuir evento) {
        //Todo almacenar el monstruo en el tile
        this.frame.setVisible(false);

        RBControladorJuego.getInstance().recibirEvento(new RBEventoNuevoTurno());
        
    }
    
    
}
